package ge;

import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;

import org.apache.log4j.Logger;
import org.codehaus.janino.ClassBodyEvaluator;
import org.codehaus.janino.CompileException;
import org.codehaus.janino.Scanner;
import org.codehaus.janino.Parser.ParseException;
import org.codehaus.janino.Scanner.ScanException;

public class SymbolClassLoader extends ClassLoader {
	private static final Logger LOG = Logger.getLogger(BNFGrammarParser.class);
	private static final HashMap<String, Class<?>> _class_cache = new HashMap<String, Class<?>>();

	public SymbolClassLoader() {
		super(SymbolClassLoader.class.getClassLoader());
	}

	public Class<?> findClass(String class_name) {
		Class<?> clazz = null;

		if (class_name.lastIndexOf('$') > 0) {
			class_name = class_name.substring(class_name.lastIndexOf('$') + 1);
		}
		else if (class_name.lastIndexOf('.') > 0) {
			class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
		}

		LOG.trace("Asked to find class: " + class_name);

		// Check the cache of created classes
		if (_class_cache.containsKey(class_name)) {
			LOG.trace("\tfound it in the cache.");
			clazz = _class_cache.get(class_name);

		} else {
			LOG.trace("\tnot found in cache, dynamically creating it.");
			try {
				ClassBodyEvaluator cbe = new ClassBodyEvaluator();
				cbe.setClassName(class_name);
				cbe.setExtendedType(NonTerminal.class);
				cbe.setParentClassLoader(ClassLoader.getSystemClassLoader());
				cbe.cook(new Scanner(null, new StringReader("")));

				clazz = cbe.getClazz();

				LOG.trace("\tDynamically created class: "
						+ clazz.getCanonicalName());

			} catch (CompileException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ScanException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// We've created the class, now we have to add it to the cache
			_class_cache.put(class_name, clazz);
		}

		return clazz;
	}
}
